<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage
-->
<head>
  <title>postMessage from about:blank, data URLs</title>
  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>        
  <script type="text/javascript" src="browserFu.js"></script>
  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=postMessage">Mozilla Bug 387706</a>
<p id="display"></p>
<div id="content" style="display: none"></div>

<pre id="test">
<script class="testbody" type="application/javascript"><![CDATA[
/** Test for Bug 387706 **/

SimpleTest.waitForExplicitFinish();

var B64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

/**
 * Encodes an array of bytes into a string using the base 64 encoding scheme.
 *
 * @param bytes
 *   An array of bytes to encode.
 */
function b64(str)
{
  var byteArray = new Array(str.length);
  for (var i = 0, sz = str.length; i < sz; i++)
    byteArray[i] = str.charCodeAt(i);

  var index = 0;
  function get3Bytes()
  {
    if (byteArray.length - index < 3)
      return null; // Less than three bytes remaining

    // Return the next three bytes in the array, and increment index for our
    // next invocation
    return byteArray.slice(index, index += 3);
  }

  var out = "";
  var bytes = null;
  while ((bytes = get3Bytes()))
  {
    var bits = 0;
    for (var i = 0; i < 3; i++)
      bits = (bits << 8) | bytes[i];
    for (var j = 18; j >= 0; j -= 6)
      out += B64_CHARS[(bits>>j) & 0x3F];
  }

  // Get the remaining bytes
  bytes = byteArray.slice(index);

  switch (bytes.length)
  {
    case 2:
      out += B64_CHARS[(bytes[0]>>2) & 0x3F] +
             B64_CHARS[((bytes[0] & 0x03) << 4) | ((bytes[1] >> 4) & 0x0F)] +
             B64_CHARS[((bytes[1] & 0x0F) << 2)] +
             "=";
      break;
    case 1:
      out += B64_CHARS[(bytes[0]>>2) & 0x3F] +
             B64_CHARS[(bytes[0] & 0x03) << 4] +
             "==";
      break;
  }

  return out;
}


var aboutBlankWindow = null;
var aboutBlank2Window = null;
var dataWindow = null;

/** Convert a nullable string to a pretty representation */
function sourceify(v)
{
  if (typeof v == "string")
    return "'" + v + "'";
  return String(v);
}

/** Receives MessageEvents to this window. */
function messageReceiver(evt)
{
  // It's not clear what the security model is for data: URLs and whether they
  // can access their parents; WebKit denies access, while Gecko currently
  // allows it.  We work around this problem by using postMessage (surprise!)
  // to start the round of tests when each iframe loads.
  if (evt.data === "next-test")
  {
    setTimeout(nextTest, 0);
    return;
  }


  try
  {
    ok(evt instanceof MessageEvent, "umm, how did we get this?");
    is(evt.type, "message", "expected events of type 'message'");
  
    if (isMozilla)
    {
      ok(evt.isTrusted === false, "shouldn't have been a trusted event");
    }
  
    if (evt.data === "about:blank-response")
    {
      // This isn't clarified in HTML5 yet, but the origin for a document which
      // has been open()ed is the origin of the calling code, somewhat loosely
      // speaking.  For the specific case of about:blank it's also possible
      // that the origin is determined by the code that opens the window.  It's
      // not codified yet which of these two causes the identifier tokens on
      // the event generated by the new window to be those of this window, but
      // in either case this is what they should be.
      is(evt.origin, "http://mochi.test:8888",
         "wrong origin for event from about:blank");
      is(evt.source, aboutBlankWindow, "wrong source");

      // ...and onto the next test
      setupBlank2();
    }
    else if (evt.data === "about:blank2-response")
    {
      is(evt.origin, "http://mochi.test:8888",
         "wrong origin for event from about:blank #2");
      is(evt.source, aboutBlank2Window, "wrong source");

      setupData();
    }
    else if (evt.data === "data-response")
    {
      // HTML5 defines the origin of a data: URI as the origin of the window or
      // script that opened the data: URI.
      is(evt.origin, "http://mochi.test:8888",
         "wrong origin for event from data URL (should be the origin of the " +
         "window/script that opened the URL, in this case the origin of this " +
         "file)");
      is(evt.source, dataWindow, "wrong source");

      finish();
    }
    else
    {
      ok(false, "unexpected message: " + evt.data);
    }
  }
  catch (e)
  {
    ok(false, "error processing event with data '" + evt.data + "': " + e);
  }
}

function getContents(description, responseText)
{
  var contents =
    "<!DOCTYPE html>\n" +
    "<html>\n" +
    "<head>\n" + 
    "  <title>about:blank</title>\n" +
    "  <script type='application/javascript'>\n" +
    "function receive(evt)\n" +
    "{\n" +
    "  var response = '" + responseText + "';\n" +
    "\n" +
    "  if (evt.source !== window.parent)\n" +
    "    response += ' wrong-source';\n" +
    "  if (evt.origin !== 'http://mochi.test:8888')\n" +
    "    response += ' wrong-origin(' + evt.origin + ')';\n" +
    "  if (evt.data !== 'from-opener')\n" +
    "    response += ' wrong-data(' + evt.data + ')';\n" +
    "\n" +
    "  window.parent.postMessage(response, 'http://mochi.test:8888');\n" +
    "}\n" +
    "\n" +
    "function ready()\n" +
    "{\n" +
    "  window.parent.postMessage('next-test', 'http://mochi.test:8888');\n" +
    "}\n" +
    "\n" +
    "window.addEventListener('load', ready, false);\n" +
    "window.addEventListener('message', receive, false);\n" +
    "  </script>\n" +
    "</head>\n" +
    "<body><p>" + description + "</p></body>\n" +
    "</html>";

  return contents;
}

function finish()
{
  SimpleTest.finish();
}

var xhtmlns = "http://www.w3.org/1999/xhtml";

function insert(el)
{
  var content = $("content");
  content.parentNode.insertBefore(el, content);
}

function setupBlank()
{
  var aboutBlankFrame = document.createElementNS(xhtmlns, "iframe");
  aboutBlankFrame.setAttribute("src", "about:blank");
  insert(aboutBlankFrame);

  aboutBlankWindow = aboutBlankFrame.contentWindow;
  var doc = aboutBlankWindow.document;
  doc.open();
  doc.write(getContents("This was about:blank #1", "about:blank-response"));
  doc.close();

  // I don't believe anything guarantees sync parsing, so we have to wait for
  // the new window to poke us to actually do the test.  :-\
}

function setupBlank2()
{
  var aboutBlank2Frame = document.createElementNS(xhtmlns, "iframe");
  aboutBlank2Frame.addEventListener("load", nextTest, false);
  aboutBlank2Frame.setAttribute("src", "about:blank");

  insert(aboutBlank2Frame);
}

// Could use window.btoa here, but that's not standardized, and we want to be
// able to run these tests against browsers that don't support it.
var dataURI = "data:text/html;base64," +
              b64(getContents("A data: URL", "data-response"));

function setupData()
{
  var dataFrame = document.createElementNS(xhtmlns, "iframe");
  dataFrame.setAttribute("src", dataURI);
  insert(dataFrame);

  dataWindow = dataFrame.contentWindow;

  // ...and wait again for the window to load...
}

var count = 0;
function nextTest()
{
  switch (count++)
  {
    case 0:
      testBlank();
      break;

    case 1:
      testBlank2();
      break;

    case 2:
      testData();
      break;

    default:
      ok(false, "unreached");
      break;
  }
}

function testBlank()
{
  aboutBlankWindow.postMessage("from-opener", "http://mochi.test:8888");
}

function testBlank2()
{
  // For some reason we can't access this across browsers prior to the iframe
  // loading, so set its value here.
  aboutBlank2Window = window.frames[1];

  var doc = aboutBlank2Window.document;

  doc.body.textContent = "This was about:blank #2";

  var script = doc.createElement("script");
  script.textContent =
    "window.parent.postMessage('about:blank2-response', " +
    "                          'http://mochi.test:8888');";
  doc.body.appendChild(script);
}

function testData()
{
  dataWindow.postMessage("from-opener", "http://mochi.test:8888");
}

window.addEventListener("message", messageReceiver, false);

addLoadEvent(setupBlank);
]]></script>
</pre>
</body>
</html>
